
#include <linux/etherdevice.h>

#include <linux/module.h>
#include <linux/printk.h>

struct vent_priv {
    int ready;
    int msg_enable;
};

static struct net_device *vnet_dev = NULL;

#define DRV_NAME		"vnet"
#define DRV_VERSION		"1.0.0"
#define EMAC_DEFAULT_MSG_ENABLE 0x0000

uint8_t mac_addr[6] = {0x00, 0x04, 0xA3, 0x12, 0x34, 0x56};

static int vnet_open(struct net_device *dev)
{
    printk("vnet, open\n");

    /* reset & init device
     * setup rx irq
     */

    /* link ready */
    netif_carrier_on(dev);
    
    /* allow transmit */
     netif_start_queue(dev);

    return 0;
}

static int vnet_stop(struct net_device *ndev)
{
    printk("vnet, stop\n");

	netif_stop_queue(ndev);
	netif_carrier_off(ndev);

    return 0;
}

static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    printk("vnet, start xmit, len: %u, data: %02X%02X%02X%02X\n", skb->len, skb->data[0], skb->data[1], skb->data[2], skb->data[3]);

    return NETDEV_TX_OK;
}

static void vnet_timeout(struct net_device *dev)
{
    printk("vnet, timeout\n");

    return;
}

static void vnet_set_rx_mode(struct net_device *ndev)
{
    printk("vnet, set rx mode, flags: %X\n", ndev->flags);

    return;
}

static int vnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
    printk("vnet, ioctl, cmd: %X\n", cmd);

    return 0;
}

static int vnet_set_mac_address(struct net_device *dev, void *p)
{
    printk("vnet, set mac addr\n");

    return 0;
}

#ifdef CONFIG_NET_POLL_CONTROLLER
static void vnet_poll_controller(struct net_device *dev)
{
    printk("vnet, poll\n");

    return;
}
#endif

static const struct net_device_ops vnet_dev_ops = {
	.ndo_open		= vnet_open,
	.ndo_stop		= vnet_stop,
	.ndo_start_xmit		= vnet_start_xmit,
	.ndo_tx_timeout		= vnet_timeout,
	.ndo_set_rx_mode	= vnet_set_rx_mode,
	.ndo_do_ioctl		= vnet_ioctl,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_set_mac_address	= vnet_set_mac_address,
#ifdef CONFIG_NET_POLL_CONTROLLER
	.ndo_poll_controller	= vnet_poll_controller,
#endif
};

static void vnet_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
    strlcpy(info->driver, DRV_NAME, sizeof(DRV_NAME));
    strlcpy(info->version, DRV_VERSION, sizeof(DRV_VERSION));
    strlcpy(info->bus_info, DRV_VERSION, sizeof(DRV_VERSION));
}

static u32 vnet_get_link(struct net_device *dev)
{
    return 0;
}

static int vnet_get_link_ksettings(struct net_device *dev, struct ethtool_link_ksettings *link_settings)
{
    return 0;
}

static int	vnet_set_link_ksettings(struct net_device *dev, const struct ethtool_link_ksettings *link_settings)
{
    return 0;
}

static u32 vnet_get_msglevel(struct net_device *dev)
{
	struct vent_priv *priv = netdev_priv(dev);

	return priv->msg_enable;
}

static void vnet_set_msglevel(struct net_device *dev, u32 value)
{
	struct vent_priv *priv = netdev_priv(dev);

	priv->msg_enable = value;
}

static const struct ethtool_ops vnet_ethtool_ops = {
	.get_drvinfo	= vnet_get_drvinfo,
	.get_link	= vnet_get_link,
	.get_link_ksettings = vnet_get_link_ksettings,
	.set_link_ksettings = vnet_set_link_ksettings,
	.get_msglevel	= vnet_get_msglevel,
	.set_msglevel	= vnet_set_msglevel,
};


static int __init vnet_init(void)
{
    int ret;
    struct net_device *ndev;
    struct vent_priv *vnet_priv;

    ndev = alloc_etherdev(sizeof(struct vent_priv));

    vnet_priv = netdev_priv(ndev);
    memset(vnet_priv, 0, sizeof(*vnet_priv));

    ndev->netdev_ops = &vnet_dev_ops;
    ndev->ethtool_ops = &vnet_ethtool_ops;
    memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);

    netif_carrier_off(ndev);

    vnet_dev = ndev;
    vnet_priv->ready = 1;
    vnet_priv->msg_enable = netif_msg_init(-1, EMAC_DEFAULT_MSG_ENABLE);

    ret = register_netdev(ndev);
	if (ret) {
		printk("vent, Registering netdev failed!\n");
        free_netdev(ndev);
		return -ENODEV;
	}

    printk("vent, dev ok\n");

    return 0;
}

static void __exit vnet_exit(void)
{
    if (vnet_dev)
    {
        unregister_netdev(vnet_dev);
        free_netdev(vnet_dev);
    }

    return;
}

module_init(vnet_init);
module_exit(vnet_exit);
